﻿using System;
//using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Collections;
using System.Windows.Forms;
namespace Caculator {
	class Expressions {
		//*
		#region Fields
		private string expression;
		private int unit = 0;
		#endregion
		//*/
		//*
		#region Properties

		public string Expression {
			get {
				return this.expression;
			}
			set {
				this.expression = value;
			}
		}

		public int Unit {
			get {
				return this.unit;
			}
			set {
				this.unit = value;
			}
		}

		#endregion
		//*/

		//*
		#region Methods

		public Expressions() {
			this.expression = "";
		}

		public Expressions(string _s) {
			this.expression = _s;
		}

		private Queue getExpressionQueue(string _expression) {
			Queue rv = new Queue();// Queue chua bieu thuc dau ra dang hau to
			Stack operatorStack = new Stack();// Stack luu tru toan tu
			StringBuilder str = new StringBuilder(_expression + ")");
			operatorStack.Push("(");
			do {
				// Gap toan hang
				if (Char.IsDigit(str[0]) || str[0] == '.') {
					int i = 0;
					StringBuilder operandBuider = new StringBuilder(_expression.Length + 1);
					while (Char.IsDigit(str[i]) || str[i] == '.') {
						operandBuider.Append(str[i]);
						i++;
					}
					str.Remove(0, i);
					rv.Enqueue(operandBuider.ToString());
				}
				// Gap toan tu
				else {
					string strCheck = str.ToString();
					string operatorCheck;
					//*
					// Gap dau ngoac mo
					if (strCheck.StartsWith("(")) {
						operatorStack.Push("(");
						str.Remove(0, 1); // Loai dau ngoac mo da xu li ra khoi bieu thuc trung to
					}
					// Gap cac phep toan cong, tru, nhan, chia
					else if (strCheck.StartsWith("+") || strCheck.StartsWith("-") || strCheck.StartsWith("*") || strCheck.StartsWith("/") || strCheck.StartsWith("%") || strCheck.StartsWith("~")) {
						operatorCheck = strCheck.Substring(0, 1);
						str.Remove(0, 1); // Loai phep toan da xu li ra khoi bieu thuc trung to
						while (getPrecedence(operatorStack.Peek().ToString()) >= getPrecedence(operatorCheck)) {
							rv.Enqueue(operatorStack.Pop().ToString());
						}
						operatorStack.Push(operatorCheck); // Day toan tu vao Stack
					}
					// Gap cac phep toan sin, cos, tan, cotan
					else if (strCheck.StartsWith("sin") || strCheck.StartsWith("cos") || strCheck.StartsWith("tan") || strCheck.StartsWith("cot")) {
						operatorCheck = strCheck.Substring(0, 3);
						str.Remove(0, 3); // Loai phep toan da xet ra khoi bieu thuc trung to
						while (getPrecedence(operatorStack.Peek().ToString()) >= getPrecedence(operatorCheck)) {
							rv.Enqueue(operatorStack.Pop().ToString());
						}
						operatorStack.Push(operatorCheck); // Day toan tu vao Stack
					}
					// Gap phep tinh can bac hai
					else if (strCheck.StartsWith("sqrt")) {
						operatorCheck = strCheck.Substring(0, 4);
						str.Remove(0, 4);
						while (getPrecedence(operatorStack.Peek().ToString()) >= getPrecedence(operatorCheck)) {
							rv.Enqueue(operatorStack.Pop().ToString());
						}
						operatorStack.Push(operatorCheck); // Day toan tu vao Stack
					}
					// Gap dau ngoac dong
					else if (strCheck.StartsWith(")")) {
						str.Remove(0, 1); // Xoa dau ngoac dong khoi bieu thuc trung to
						while (operatorStack.Peek().ToString() != "(") {
							rv.Enqueue(operatorStack.Pop().ToString());
						}
						operatorStack.Pop();
					}
					else {
						rv.Clear();
						return rv;
					}
					//*/					
				}
			} while (operatorStack.Count != 0);
			return rv;
		}

		public double evaluateExpression() {
			Stack stack = new Stack();
			Queue queue = getExpressionQueue(expression);
			double operand1 = 0.0, operand2 = 0.0, result = 0.0;
			if (queue.Count == 0) return Double.NaN;
			while (queue.Count != 0) {
				string str = queue.Dequeue().ToString();
				switch (str) {
					case "+":
					case "-":
					case "*":
					case "/":
					case "%": {
						try {
							operand1 = Double.Parse(stack.Pop().ToString());
							operand2 = Double.Parse(stack.Pop().ToString());
						} catch {
							return Double.NaN;
						}
					}
					break;
					case "~":
					case "sin":
					case "cos":
					case "tan":
					case "cot":
					case "sqrt": {
						try {
							operand1 = Double.Parse(stack.Pop().ToString());
						} catch {
							return Double.NaN;
						}
					} break;
					default : operand1 = Double.Parse(str); break;
				}
				switch (str) {
					case "+": result = operand2 + operand1; break;
					case "-": result = operand2 - operand1; break;
					case "*": result = operand2 * operand1; break;
					case "/": result = operand2 / operand1; break;
					case "%": result = operand2 % operand1; break;
					case "~": result = -operand1; break;
					case "sin": {
						if (unit == 0) {
							operand1 *= Math.PI / 180.0;	
						}
						if (unit == 2) {
							operand1 *= Math.PI / 200.0; 
						}
						result = Math.sin(operand1);
					} break;
					case "cos": {
						if (unit == 0) {
							operand1 *= Math.PI / 180.0;
						}
						if (unit == 2) {
							operand1 *= Math.PI / 200;
						}
						result = Math.cos(operand1); break;
					}
					case "tan": {
						if (unit == 0) {
							operand1 *= Math.PI / 180.0;
						}
						if (unit == 2) {
							operand1 *= Math.PI / 200;
						}
						result = Math.tan(operand1); break;
					}
					case "cot": {
						if (unit == 0) {
							operand1 *= Math.PI / 180.0;
						}
						if (unit == 2) {
							operand1 *= Math.PI / 200;
						}
						result = Math.cot(operand1);
					} break;
					case "sqrt": {
						result = Math.sqrt(operand1);
					} break;
					default : result = operand1;break;
				}
				if (Double.IsNaN(result) || Double.IsInfinity(result)) {
					return Double.NaN;
				}
				else {
					stack.Push(result.ToString());
				}
			}
			return Double.Parse(stack.Peek().ToString()); ;
		}

		// Ham lay thu tu uu tien cua toan tu
		private static int getPrecedence(string _operator) {
			int rv = 0;
			switch (_operator) {
				case "+":
				case "-": rv = 2; break;
				case "*":
				case "/":
				case "%": rv = 3; break;
				case "sin":
				case "cos":
				case "tan":
				case "cot": 
				case "sqrt": rv = 4; break;
				case "~": rv = 5; break;
			}
			return rv;
		}

		#endregion
		//*/
	}
}
